package org.cleverframe.core.fileupload.controller;

import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang3.StringUtils;
import org.cleverframe.common.controller.BaseController;
import org.cleverframe.common.persistence.Page;
import org.cleverframe.common.utils.Encodes;
import org.cleverframe.common.vo.AjaxMessage;
import org.cleverframe.core.fileupload.FileuploadBeanNames;
import org.cleverframe.core.fileupload.FileuploadJspUrlPath;
import org.cleverframe.core.fileupload.attributes.FileuploadSessionAttributes;
import org.cleverframe.core.fileupload.entity.FileInfo;
import org.cleverframe.core.fileupload.service.FileManagerService;
import org.cleverframe.core.fileupload.service.IStorageService;
import org.cleverframe.core.fileupload.vo.FileuploadProgress;
import org.cleverframe.webui.easyui.data.DataGridJson;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;

/**
 * 文件管理器<br>
 * 
 * @author LiZW
 * @version 2015年9月17日 上午11:09:24
 */
@Controller
@RequestMapping("/${mvcPath}/fileupload")
@Transactional(readOnly = true)
public class FileManagerController extends BaseController
{
    /** 日志对象 */
    private final static Logger logger = LoggerFactory.getLogger(FileManagerController.class);
    
    @Autowired
    @Qualifier(FileuploadBeanNames.LocalStorageService)
    private IStorageService storageService;

    @Autowired
    @Qualifier(FileuploadBeanNames.FileManagerService)
    private FileManagerService fileManagerService;
    
    /**
     * 文件上传Demo页
     */
    @RequestMapping("/demo")
    public ModelAndView getFileuploadJsp(HttpServletRequest request, HttpServletResponse response)
    {
        ModelAndView mav = new ModelAndView(FileuploadJspUrlPath.FileuploadDemo);
        return mav;
    }
    
    /**
     * 上传文件管理页面 
     */
    @RequestMapping("/fileManager")
    public ModelAndView getFileManagerJsp(HttpServletRequest request, HttpServletResponse response)
    {
        ModelAndView mav = new ModelAndView(FileuploadJspUrlPath.FileManager);
        return mav;
    }
    
    /**
     * 通过文件签名实现文件秒传，只能一次上传一个文件<br>
     * 文件秒传：实际上是通过文件签名在以前上传的文件中找出一样的文件，并未真正的上传文件<br>
     * <b>注意：文件秒传取决于此文件已经上传过了而且使用的文件签名类型相同</b>
     * 
     * @param fileName 文件名称
     * @param fileDigest 文件签名
     * @param digestType 文件签名类型，目前只支持MD5和SHA1两种
     * @throws IOException 
     */
    @ResponseBody
    @RequestMapping("/uploadLazy")
    @Transactional(readOnly = false)
    public AjaxMessage fileuploadLazy(
            HttpServletRequest request, 
            HttpServletResponse response,
            @RequestParam(value = "fileName") String fileName,
            @RequestParam(value = "fileDigest") String fileDigest,
            @RequestParam(value = "digestType") String digestType) throws Exception
    {
        AjaxMessage ajaxMessage = new AjaxMessage();
        // 验证文件签名
        boolean digestValidateFlag = true; // 验证成功标识
        Character type = null;// 签名类型
        if(StringUtils.isBlank(fileName) || StringUtils.isBlank(fileDigest) || StringUtils.isBlank(digestType))
        {
            ajaxMessage.setMessage("fileName、fileDigest、digestType参数不能为空");
            digestValidateFlag = false;
        }
        else
        {
            fileDigest = fileDigest.toLowerCase();
            // 验证是否是Hex编码
            if(Encodes.isHexcode(fileDigest) == false)
            {
                ajaxMessage.setMessage("文件签名必须使用Hex编码");
                digestValidateFlag = false;
            }
            // 验证文件签名长度
            if ("MD5".equalsIgnoreCase(digestType))
            {
                type = FileInfo.MD5_DIGEST;
                // 验证长度MD5 长度为：32
                if(fileDigest.length() != 32)
                {
                    ajaxMessage.setMessage("文件签名长度不正确(MD5签名长度32，SHA1签名长度40)");
                    digestValidateFlag = false;
                }
            }
            else if ("SHA1".equalsIgnoreCase(digestType))
            {
                type = FileInfo.SHA1_DIGEST;
                // 验证长度SHA1 长度为：40
                if(fileDigest.length() != 40)
                {
                    ajaxMessage.setMessage("文件签名长度不正确(MD5签名长度32，SHA1签名长度40)");
                    digestValidateFlag = false;
                }
            }
            else
            {
                ajaxMessage.setMessage("不支持的文件签名：" + digestType);
                digestValidateFlag = false;
            }
        }
        
        // 验证通过
        if(digestValidateFlag)
        {
            FileInfo fileInfo = storageService.lazySaveFile(fileName, fileDigest, type);
            if(fileInfo == null)
            {
                ajaxMessage.setMessage("此文件使用此种文件签名秒传失败，服务器找不到此文件签名");
            }
            else
            {
                ajaxMessage.setSuccess(true);
                ajaxMessage.setObject(fileInfo);
            }
        }
        return ajaxMessage;
    }
    
    /**
     * 文件上传存储到当前服务器磁盘示例，支持多文件上传<br>
     * <b>注意：建议先使用fileuploadLazyToLocal方法进行秒传，秒传失败再使用此方法</b>
     */
    @ResponseBody
    @RequestMapping("/upload")
    @Transactional(readOnly = false)
    public AjaxMessage fileupload(HttpServletRequest request, HttpServletResponse response)
    {
        AjaxMessage ajaxMessage = new AjaxMessage();
        // 保存上传文件
        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
        long start = System.currentTimeMillis();
        Iterator<String> fileNames = multipartRequest.getFileNames();
        long end = System.currentTimeMillis();
        // 计算上传时间
        long uploadTime = end - start;
        logger.debug("上传用时：" + uploadTime + "ms");
        List<FileInfo> fileInfoList = new ArrayList<FileInfo>();
        while (fileNames.hasNext())
        {
            String name = fileNames.next();
            MultipartFile mFile = multipartRequest.getFile(name);
            if(mFile.isEmpty())
            {
                continue;
            }
            FileInfo fileInfo = new FileInfo();
            try
            {
                fileInfo = storageService.saveFile(uploadTime, mFile);
                fileInfoList.add(fileInfo);
            }
            catch (Exception e)
            {
                logger.error("文件上传失败", e);
                e.printStackTrace();
            }
            finally
            {
                
            }
        }
        if(fileInfoList.size() > 0)
        {
            ajaxMessage.setSuccess(true);
            ajaxMessage.setObject(fileInfoList);
            
            // 处理上传进度
            HttpSession session = request.getSession();
            FileuploadProgress fileuploadProgress = (FileuploadProgress) session.getAttribute(FileuploadSessionAttributes.FILEUPLOAD_PROGRESS);
            if (fileuploadProgress != null)
            {
                fileuploadProgress.setComplete(true);
            }
        }
        else
        {
            ajaxMessage.setMessage("此请求没有上传文件");
        }
        return ajaxMessage;
    }
    
    /**
     * 获取文件上传进度信息，若上传完成则从Session中移除进度信息<br>
     */
    @ResponseBody
    @RequestMapping("/fileuploadProgress")
    public AjaxMessage fileuploadStat(HttpServletRequest request, HttpServletResponse response)
    {
        AjaxMessage ajaxMessage = new AjaxMessage();
        HttpSession session = request.getSession();
        FileuploadProgress fileuploadProgress = (FileuploadProgress) session.getAttribute(FileuploadSessionAttributes.FILEUPLOAD_PROGRESS);
        ajaxMessage.setObject(fileuploadProgress);
        if (fileuploadProgress != null && fileuploadProgress.isComplete())
        {
            session.removeAttribute(FileuploadSessionAttributes.FILEUPLOAD_PROGRESS);
        }
        return ajaxMessage;
    }

    /**
     * 下载上传文件<br>
     * */
    @ResponseBody
    @RequestMapping("/fileDownload/{uuid}")
    public AjaxMessage fileDownload(
            HttpServletRequest request, 
            HttpServletResponse response, 
            @PathVariable(value = "uuid") String uuid)
    {
        AjaxMessage ajaxMessage = new AjaxMessage();
        try
        {
            FileInfo fileInfo = storageService.isExists(uuid);
            if (fileInfo == null)
            {
                ajaxMessage.setMessage("文件不存在！");
                return ajaxMessage;
            }
            // 文件存在，下载文件
            String filename = Encodes.browserDownloadFileName(request.getHeader("User-Agent"), fileInfo.getFileName());
            response.setContentType("multipart/form-data");
            // response.setHeader("Content-Disposition", "attachment;");
            response.setHeader("Content-Disposition", "attachment;fileName=" + filename);
            response.setHeader("Content-Length", fileInfo.getFileSize().toString());
            OutputStream outputStream = response.getOutputStream();
            fileInfo = storageService.openFile(uuid, outputStream);
            // outputStream.close(); //Servlet容器会关闭
            return null;
        }
        catch (Exception e)
        {
            logger.info("下载文件取消或失败");
        }
        return ajaxMessage;
    }
    
    /**
     * 删除文件<br>
     * @param id 文件信息ID
     * @param lazy 如果值为true表示：只删除当前文件引用；值为false表示：如果没有其他引用才删除服务器端文件
     */
    @ResponseBody
    @RequestMapping("/delete/{uuid}")
    @Transactional(readOnly = false)
    public AjaxMessage fileDelete(
            HttpServletRequest request, 
            HttpServletResponse response, 
            @PathVariable(value = "uuid") String uuid,
            @RequestParam(value = "lazy", required = false, defaultValue = "true") boolean lazy)
    {
        AjaxMessage ajaxMessage = new AjaxMessage();
        try
        {
            int result = storageService.deleteFile(uuid, lazy);
            ajaxMessage.setSuccess(true);
            switch (result)
            {
            case 1:
                ajaxMessage.setMessage("删除文件引用与文件");
                break;
            case 2:
                ajaxMessage.setMessage("删除文件引用，未删除文件");
                break;
            case 3:
                ajaxMessage.setMessage("文件不存在");
                break;
            }
        }
        catch (Exception e)
        {
            ajaxMessage.setMessage("操作异常");
            ajaxMessage.setExceptionMessage(e.getMessage());
        }
        return ajaxMessage;
    }
    
    /**
     * 获取文件信息
     * @param id 文件信息ID
     * @throws IOException 
     */
    @ResponseBody
    @RequestMapping("/getFileInfo/{uuid}")
    public AjaxMessage getFileInfo(
            HttpServletRequest request, 
            HttpServletResponse response, 
            @PathVariable(value = "uuid") String uuid) throws Exception
    {
        AjaxMessage ajaxMessage = new AjaxMessage();
        FileInfo fileInfo = storageService.isExists(uuid);
        if (fileInfo == null)
        {
            ajaxMessage.setMessage("文件不存在");
        }
        else
        {
            ajaxMessage.setSuccess(true);
            ajaxMessage.setMessage("文件存在");
            ajaxMessage.setObject(fileInfo);
        }
        return ajaxMessage;
    }
    
    /**
     * 查询上传的文件，使用分页<br>
     * @return EasyUI DataGrid控件的json数据
     */
    @ResponseBody
    @RequestMapping("/findFileInfo")
    public DataGridJson<FileInfo> findFileInfo(HttpServletRequest request, HttpServletResponse response,
            @RequestParam(value = "digest-search", required = false, defaultValue = "") String digest,
            @RequestParam(value = "fileName-search", required = false, defaultValue = "") String fileName,
            @RequestParam(value = "newName-search", required = false, defaultValue = "") String newName,
            @RequestParam(value = "startTime-search", required = false, defaultValue = "") Date startTime,
            @RequestParam(value = "endTime-search", required = false, defaultValue = "") Date endTime)
    {
        DataGridJson<FileInfo> json = new DataGridJson<FileInfo>();
        Page<FileInfo> page = fileManagerService.findFileInfoByPage(new Page<FileInfo>(request, response), digest, fileName, newName, startTime, endTime);
        json.setRows(page.getList());
        json.setTotal(page.getCount());
        return json;
    }
}
